package com.gitee.wsl.compose.chart.geometry.model

import com.gitee.wsl.common.ui.base.Vector2D
import com.gitee.wsl.common.ui.base.distance

object BezierPath {
    /**
     * from http://paulbourke.net/geometry/bezier/index.html
     * Three control Vector2D Bezier interpolation
     * mu ranges from 0 to 1, start to end of the curve
     */
    fun bezier3(p1: Vector2D, p2: Vector2D, p3: Vector2D, mu: Double): Vector2D {
        val mum1: Double
        val mum12: Double
        val mu2: Double
        val p = Vector2D()
        mu2 = mu * mu
        mum1 = 1 - mu
        mum12 = mum1 * mum1
        p.x = (p1.x * mum12 + 2 * p2.x * mum1 * mu + p3.x * mu2).toFloat()
        p.y = (p1.y * mum12 + 2 * p2.y * mum1 * mu + p3.y * mu2).toFloat()
        //p.z = p1.z * mum12 + 2 * p2.z * mum1 * mu + p3.z * mu2;
        return p
    }

    /**
     * from http://paulbourke.net/geometry/bezier/index.html
     * Four control Vector2D Bezier interpolation
     * mu ranges from 0 to 1, start to end of curve
     */
    fun cubicBezier(p1: Vector2D, p2: Vector2D, p3: Vector2D, p4: Vector2D, mu: Double): Vector2D {
        val mum1: Double
        val mum13: Double
        val mu3: Double
        val p = Vector2D()
        mum1 = 1 - mu
        mum13 = mum1 * mum1 * mum1
        mu3 = mu * mu * mu
        p.x = (mum13 * p1.x + 3 * mu * mum1 * mum1 * p2.x + 3 * mu * mu * mum1 * p3.x + mu3 * p4.x).toFloat()
        p.y = (mum13 * p1.y + 3 * mu * mum1 * mum1 * p2.y + 3 * mu * mu * mum1 * p3.y + mu3 * p4.y).toFloat()
        //p.z = mum13*p1.z + 3*mu*mum1*mum1*p2.z + 3*mu*mu*mum1*p3.z + mu3*p4.z;
        return p
    }

    /**
     * from geodroid
     * FIXME
     */
    fun cubicSplineControlPoints(coords: Array<Vector2D>, alpha: Float): List<Vector2D> {
        require(!(alpha < 0.0 || alpha > 1.0)) { "alpha must be between 0 and 1 inclusive" }
        require(!(coords.size < 2)) { "number of Points must be >= 2" }
        val n = coords.size
        val ctrl: MutableList<Vector2D> = ArrayList()
        var curr = Vector2D(2 * coords[0].x - coords[1].x, 2 * coords[0].y - coords[1].y)
        var next = coords[0]
        val mid = Vector2D()
        mid.x = ((curr.x + next.x) / 2.0).toFloat()
        mid.y = ((curr.y + next.y) / 2.0).toFloat()
        val midPrev = Vector2D()
        val last = Vector2D(
            2 * coords[n - 1].x - coords[n - 2].x,
            2 * coords[n - 1].y - coords[n - 2].y
        )
        val anchor = Vector2D()
        var dv = curr.distance(next)
        for (i in 0 until n) {
            curr = next
            next = if (i < n - 1) coords[i + 1] else last
            midPrev.x = mid.x
            midPrev.y = mid.y
            mid.x = ((curr.x + next.x) / 2.0).toFloat()
            mid.y = ((curr.y + next.y) / 2.0).toFloat()
            val dvPrev = dv
            dv = curr.distance(next)
            val p = dvPrev / (dvPrev + dv)
            anchor.x = (midPrev.x + p * (mid.x - midPrev.x)).toFloat()
            anchor.y = (midPrev.y + p * (mid.y - midPrev.y)).toFloat()
            val dx = (anchor.x - curr.x).toDouble()
            val dy = (anchor.y - curr.y).toDouble()
            if (i > 0) {
                ctrl.add(
                    Vector2D(
                        (alpha * (curr.x - midPrev.x + dx) + midPrev.x - dx).toFloat(),
                        (alpha * (curr.y - midPrev.y + dy) + midPrev.y - dy).toFloat()
                    )
                )
            }
            if (i < n - 1) {
                ctrl.add(
                    Vector2D(
                        (alpha * (curr.x - mid.x + dx) + mid.x - dx).toFloat(),
                        (alpha * (curr.y - mid.y + dy) + mid.y - dy).toFloat()
                    )
                )
            }
        }
        return ctrl
    }
}

